package com.service.impl;

import com.domain.Permission;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.mapper.PermissionMapper;
import com.qo.QueryObject;
import com.service.PermissionService;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * @author shkstart
 * @create 2022/3/11-11:30
 */
@Transactional
@Service
public class PermissionServiceImpl implements PermissionService {
    @Autowired
    private PermissionMapper mapper;

    @Override
    public void delete(Long id) {
        mapper.deleteByPrimaryKey(id);
    }

    @Override
    public void save(Permission permission) {
        mapper.insert(permission);
    }

    @Override
    public Permission get(Long id) {
        return mapper.selectByPrimaryKey(id);
    }

    @Override
    public List<Permission> listAll() {
        return mapper.selectAll();
    }

    @Override
    public void update(Permission permission) {
        mapper.updateByPrimaryKey(permission);
    }

    @Override
    public PageInfo<Permission> query(QueryObject qo) {
        PageHelper.startPage(qo.getCurrentPage(), qo.getPageSize());
        List<Permission> list = mapper.selectLimitList(qo);
        return new PageInfo(list);
    }

    @Autowired
    private ApplicationContext ctx;  //spring容器对象
    @Override
    public void load() {
        //需求：将每个控制器中添加了注解的方法转化为权限数据，并插入数据库
        // 定义一个方法，可以获取到数据库中所有的权限表达式（方便后面去重）
        List<String> permissions = mapper.selectAllExpression();

        // 1、获取所有控制器（根据注解查询更方便）利用spring容器对象,获取带有controller注解的所有bean
        // Map<String, Object> map 里面的 key 为bean标签的id，就是首字母小写的类名；value 就是容器中的对象
        Map<String, Object> map = ctx.getBeansWithAnnotation(Controller.class);
        Collection<Object> beans = map.values();

        // 2、遍历获取每个控制器中的方法
        for (Object controller : beans) {
            //判断实例是否是Cglib的代理对象,如果不是，说明没有使用shiro的权限验证注解
            if (!AopUtils.isCglibProxy(controller)) {
                continue;//执行下一次循环
            }
            //获取controller的字节码对象(获取该类的父类)
            Class clazz = controller.getClass().getSuperclass();
            //Class<?> clazz = controller.getClass();        //控制器的字节码对象
            Method[] methods = clazz.getDeclaredMethods();   //通过反射拿到里面所有的方法
            // 3、遍历获取到的每个方法
            for (Method method : methods) {
                // 4、判断方法上是否贴有shiro权限验证注解
                //RequiredPermission annotation = method.getAnnotation(RequiredPermission.class);
                RequiresPermissions annotation = method.getAnnotation(RequiresPermissions.class);
                //判断注解是否为空
                if(annotation != null){
                    // 5、从注解中获取权限相关数据，并封装为权限对象
                    String name = annotation.value()[1];
                    // 方式一：从注解中获取权限表达式
                    String expression = annotation.value()[0];
                    // 方式二：通过反射拼接 获取权限表达式
                    //    获取类名DepartmentController，去掉Controller，得到 Department
                    //String simpleName = clazz.getSimpleName().replace("Controller","");
                    //     uncapitalize方法可以把首字母变为小写，得到 department +":"+method.getName()
                    // String expression = StringUtils.uncapitalize(simpleName)+":"+method.getName();

                    // 6、判断数据库是否已存在，若无就插入
                    //在前面级联关联关系时，为了不出现重复，是将所有关系都删除再重新插入，没有判断是否存在
                    //而这里没有进行此种操作。是因为如果重新插入，就会导致 id列发生变化，就会导致permission_id表数据错误
                    if(!permissions.contains(expression)) { //permissions、expression 类型要保持一致
                        //封装成权限对象
                        Permission permission = new Permission();
                        permission.setName(name);
                        permission.setExpression(expression);
                        //把权限对象保存到数据库
                        mapper.insert(permission);
                    }
                }
            }
        }
    }

    @Override
    public List<String> selectByEmpId(Long id) {
        return mapper.selectExpByEmpId(id);
    }
}
